Android中一张图片占用的内存大小

您所在的位置:网站首页 android graphics内存 Android中一张图片占用的内存大小

Android中一张图片占用的内存大小

#Android中一张图片占用的内存大小| 来源: 网络整理| 查看: 265

最近面试过程中发现对Android中一些知识有些模棱两可,之前总是看别人的总结,自己没去实践过,这两天对个别问题进行专门研究

探讨:如何计算Android中一张图片占据内存的大小

解释:此处说的占据内存,是APP加载图片用的内存,即APP运行时图片占用的内存,不考虑图片显示到界面占用的内存(即只加载图片,不显示。因为显示图片时,系统有可能根据控件的大小或其他因素对内存进行了优化,不确定因素太多),同时也不考虑使用三方库加载图片占用的内存,如glide、等三方图片库,三方库在加载图片的过程中有可能对图片进行了优化,会 1则,加载到内存 图片被放大,否则被缩小 inDensity 只与drawable对应的目录有关,即drawable、drawable-lpi、drawable-mdpi、drawable-hdpi等 inTargetDensity 只与硬件设备有关 图片越往高密度目录放置,占用内存越小

图片占用内存 = bitmap 宽 * bitmap 高 * 每个像素占用的字节数 bitmap 宽 = 原图的宽 * (设备的 dpi / 目录对应的 dpi) bitmap 高 = 原图的高 * (设备的 dpi / 目录对应的 dpi)

注意 :如果 BitmapFactory.Options options = new BitmapFactory.Options(); 中 options.inSampleSize != 0 ,则以上公式中。默认options.inSampleSize为0,源码中关于此属性有一段注释,如果为0,则nativate层按1来取值 inSampleSize > 1时

bitmap 宽 = (原图宽 / inSampleSize) * (inTargetDensity / inDensity) bitmap 高 = (原图高 / inSampleSize) * (inTargetDensity / inDensity)

(注意inSampleSize只能是2的幂,如不是2的幂下转到最大的2的幂,而且inSampleSize>=1) BitmapFactory.decodeResource 加载的图片可能会经过缩放 查看android 2.3的系统源码 可以发现 float scale = targetDensity / (float)density 相关的信息。android 比较高的系统(8.0 9.0),Bitmap分配内存相关的细节放到了native层,没有细看。如下是2.3系统相关的代码 https://www.androidos.net.cn/android/2.3.7_r1/xref/frameworks/base/graphics/java/android/graphics/BitmapFactory.java

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) { // we don't throw in this case, thus allowing the caller to only check // the cache, and not force the image to be decoded. if (is == null) { return null; } // we need mark/reset to work properly if (!is.markSupported()) { is = new BufferedInputStream(is, 16 * 1024); } // so we can call reset() if a given codec gives up after reading up to // this many bytes. FIXME: need to find out from the codecs what this // value should be. is.mark(1024); Bitmap bm; if (is instanceof AssetManager.AssetInputStream) { bm = nativeDecodeAsset(((AssetManager.AssetInputStream) is).getAssetInt(), outPadding, opts); } else { // pass some temp storage down to the native code. 1024 is made up, // but should be large enough to avoid too many small calls back // into is.read(...) This number is not related to the value passed // to mark(...) above. byte [] tempStorage = null; if (opts != null) tempStorage = opts.inTempStorage; if (tempStorage == null) tempStorage = new byte[16 * 1024]; bm = nativeDecodeStream(is, tempStorage, outPadding, opts); } return finishDecode(bm, outPadding, opts); } private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) { if (bm == null || opts == null) { return bm; } final int density = opts.inDensity; if (density == 0) { return bm; } bm.setDensity(density); final int targetDensity = opts.inTargetDensity; if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) { return bm; } byte[] np = bm.getNinePatchChunk(); final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); if (opts.inScaled || isNinePatch) { float scale = targetDensity / (float)density; // TODO: This is very inefficient and should be done in native by Skia final Bitmap oldBitmap = bm; bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f), (int) (bm.getHeight() * scale + 0.5f), true); oldBitmap.recycle(); if (isNinePatch) { np = nativeScaleNinePatch(np, scale, outPadding); bm.setNinePatchChunk(np); } bm.setDensity(targetDensity); } return bm; } 设备dpi

即设备(这里指手机)的像素密度,代码里指 inTargetDensity,只跟设备有关 例如:买手机时候查看手机屏幕分辨率(像素) 1080 * 1920 , 5.2 英寸 (手机的对角线长度) 一英寸 = 2.54 cm 则dpi = (根号下(1080 * 1080 + 1920 * 1920))/ 5.2 = 423.6 取近似值 480

目录对应的 dpi

代码里指 inDensity 目录名称与 inDensity 的对应关系如下 drawable 没带后缀对应的inDensity为 160 dpi

ldpi ——> 120 即图片放在ldpi目录时,inDensity 为 120mdpi ——> 160 即图片放在mdpi目录时,inDensity 为 160hdpi ——> 240 …xhdpi ——> 320 …xxhdpi ——> 480 …xxxhdpi ——> 640 … 在这里插入图片描述

将一张图片放入不同的res目录中 ldpi,mdpi 等,可以通过下面方式获取相应目录的dpi

private int getTargetDensityByResource(Resources resources, int id) { TypedValue value = new TypedValue(); resources.openRawResource(id, value); Log.d("LuoYer", "value.density: " + value.density); return value.density; } Android中RGB编码格式(整型编码) RGB888(int):R、G、B分量各占8位RGB565(short):R、G、B分量分别占5、6、5位RGB555(short):RGB分量都用5位表示(剩下的1位不用)ARGB8888(int):A、R、G、B分量各占8位ARGB4444(short):A、R、G、B分量各占4位 android 加载bitmap时,默认编码格式 为 ARGB8888,故图片的每个像素点占 4个字节

代码:

BitmapFactory.Options options = new BitmapFactory.Options(); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.weixin, options); DisplayMetrics metrics = new DisplayMetrics(); WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(metrics); Log.i(TAG, "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount()); Log.i(TAG, "width:" + bitmap.getWidth() + ":::height:" + bitmap.getHeight()); Log.i(TAG, "inDensity:" + options.inDensity + ":::inTargetDensity:" + options.inTargetDensity); //Log.i(TAG, "imageview.width:" + imageView.getWidth() + ":::imageview.height:" + imageView.getHeight()); Log.i(TAG, "loadResImage: options.inSampleSize:" + options.inSampleSize); Log.i(TAG, "loadResImage: " + metrics.density); Log.i(TAG, "loadResImage: " + metrics.heightPixels); Log.i(TAG, "loadResImage: " + metrics.widthPixels); 手机SD卡中的图片,加载到内存时,占用的内存大小

所得结论:

图片占用内存 = 原图宽 * 原图高 * 每个像素占用的字节数

同res中的资源 :如果 BitmapFactory.Options options = new BitmapFactory.Options(); 中 options.inSampleSize != 0 ,则以上公式中。默认options.inSampleSize为0,源码中关于此属性有一段注释,如果为0,则nativate层按1来取值 inSampleSize > 1时

bitmap 宽 = (原图宽 / inSampleSize) * 每个像素占用的字节数 bitmap 高 = (原图高 / inSampleSize) * 每个像素占用的字节数 图片占用内存 = bitmap 宽 * bitmap 高 * 每个像素占用的字节数

(注意inSampleSize只能是2的幂,如不是2的幂下转到最大的2的幂,而且inSampleSize>=1) 对于一个file或者stream那么inDensity和inTargetDensity是不考虑的!他们默认就是0,可以理解为 (inTargetDensity / inDensity) = 1。

代码:

BitmapFactory.Options options = new BitmapFactory.Options(); String str = Environment.getExternalStorageDirectory() + "/Pictures/Screenshots/Screenshot_20200221-110020.jpg"; Bitmap bitmap = BitmapFactory.decodeFile(str); DisplayMetrics metrics = new DisplayMetrics(); WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(metrics); Log.i(TAG, "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount()); Log.i(TAG, "width:" + bitmap.getWidth() + ":::height:" + bitmap.getHeight()); Log.i(TAG, "inDensity:" + options.inDensity + ":::inTargetDensity:" + options.inTargetDensity); //Log.i(TAG, "imageview.width:" + imageView.getWidth() + ":::imageview.height:" + imageView.getHeight()); Log.i(TAG, "loadResImage: options.inSampleSize:" + options.inSampleSize); Log.i(TAG, "loadResImage: " + metrics.density); Log.i(TAG, "loadResImage: " + metrics.heightPixels); Log.i(TAG, "loadResImage: " + metrics.widthPixels); 注意:一定要动态申请SD卡读写权限,否则获取到的bitmap是null的,小编刚开始没有动态申请权限(5.0系统的手机除外),判断File 一直存在,但就是 bitmap获取为null,一会找不到问题所在,后来想到权限的问题,解决了

getByteCount() 获取的单位为 Byte(简称B),即字节,一个字节为 8bite,即8比特(简称b), 1M = 1024 KB = 1024 * 1024 B

位深与色深

我们知道了图片在内存中和在磁盘上的两种不同的表示形式:前者为Bitmap,后者为各种压缩格式。这里介绍一下位深与色深的概念: ①色深 色深指的是每一个像素点用多少bit来存储ARGB值,属于图片自身的一种属性。色深可以用来衡量一张图片的色彩处理能力(即色彩丰富程度)。 典型的色深是8-bit、16-bit、24-bit和32-bit等。 上述的Bitmap.Config参数的值指的就是色深。比如ARGB_8888方式的色深为32位,RGB_565方式的色深是16位。

②位深 位深指的是在对Bitmap进行压缩存储时存储每个像素所用的bit数,主要用于存储。由于是“压缩”存储,所以位深一般小于或等于色深 。 举个例子:某张图片100像素*100像素 色深32位(ARGB_8888),保存时位深度为24位,那么: 该图片在内存中所占大小为:100 * 100 * (32 / 8) Byte 在文件中所占大小为 100 * 100 * ( 24/ 8 ) * 压缩率 Byte

步骤

1、使用自己的手机截屏,保存图片,获取一张 1080 * 1920的图片,查看其sd卡路径 strPath。同时导入到AS 开发的app中 drawable目录下 在这里插入图片描述 位深24 这个位深只与图片在硬盘上占用空间有关,与加载到手机内存占用无关 2、获取bitmap getByteCount(),打印输出 图片放drawable目录

private void loadResImage() { BitmapFactory.Options options = new BitmapFactory.Options(); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.weixin, options); Log.i(TAG, "loadResImage: " + Environment.getExternalStorageDirectory()); String str = Environment.getExternalStorageDirectory() + "/Pictures/Screenshots/Screenshot_20200221-110020.jpg"; //Bitmap bitmap = BitmapFactory.decodeFile(str); //Log.i(TAG, "loadResImage: "+str); File file = new File(str); Log.i(TAG, "loadResImage: file:" + file); Log.i(TAG, "loadResImage: " + file.exists()); //imageView.setImageBitmap(bitmap); DisplayMetrics metrics = new DisplayMetrics(); WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(metrics); Log.i(TAG, "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount()); Log.i(TAG, "width:" + bitmap.getWidth() + ":::height:" + bitmap.getHeight()); Log.i(TAG, "inDensity:" + options.inDensity + ":::inTargetDensity:" + options.inTargetDensity); //Log.i(TAG, "imageview.width:" + imageView.getWidth() + ":::imageview.height:" + imageView.getHeight()); Log.i(TAG, "loadResImage: options.inSampleSize:" + options.inSampleSize); Log.i(TAG, "loadResImage: " + metrics.density); Log.i(TAG, "loadResImage: " + metrics.heightPixels); Log.i(TAG, "loadResImage: " + metrics.widthPixels); Display display = getWindowManager().getDefaultDisplay(); Point point = new Point(); display.getSize(point); int width = point.x; int height = point.y; Log.i(TAG, "loadResImage: " + width); Log.i(TAG, "loadResImage: " + height); }

日志输出: 在这里插入图片描述 注意:为何打印屏幕分辨率时,本来是1920,却打印了1792,因为华为手机有虚拟导航栏,打开app前将 虚拟导航栏隐藏,再打开应用,就可以看到是1920了,计算时仍使用1920 计算: 使用公式1 图片内存占用大小:3240 * 5760 * 4 = 74649600 B = 72900 KB = 71.1914 MB 使用公式2 图片内存占用大小:1080 * (480 / 160)* 1920 * (480 / 160)* 4= 74649600 B = 72900 KB = 71.1914 MB

图片放SD卡 在这里插入图片描述 计算: 1080 * 1920 * 4 = 8294400 B

图片放xxhdpi ,因为 inDensity 和 inTargetDensity 相等,故图片不需要缩放,内存大小相当于从SD卡中加载 在这里插入图片描述 放xxxhdpi 在这里插入图片描述 为什么买的硬盘总会比标识的容量小那么一些呢? 硬盘生产厂商并不是以我们的1KB=1024byte去计算的,而是1KB=1000byte计算的,因此我们会觉得总是给少了。 参考: Android Bitmap(位图)详解 Android中一张图片占据的内存大小是如何计算 Android高效内存1:一张图片占用多少内存 Android高效内存2:让图片占用尽可能少的内存 Android开发之高效加载Bitmap Android: BitmapFactory.decodeResource BitmapFactory.decodeStream https://www.androidos.net.cn/sourcecode android 系统源码 BitmapFactory.decodeResource 和 BitmapFactory.decodeStream in Android区别 BitmapFactory.Options中的inDensity和inTargetDensity BitmapFactory.Options中的inDensity,inTargetDensity,inScreenDensity详解 decode图片时BitmapFactory.Options中的inDensity和inTargetDensity Android 屏幕真实分辨率获取 android BitMap回收 Android bitmap(三) BitmapFactory inSampleSize Android中一张图片占用的内存大小 Android 开发绕不过的坑:你的 Bitmap 究竟占多大内存? 从源码角度讲解 Bitmap知识点 Android Bitmap计算大小 getRowBytes和getByteCount() 字节换算(byte-to-bit) b ,B,KB,MB,GB之间的关系 内部由LinkedHashMap来存储数据,size大小不能小于0。注意:accessOrder传入的值为true,说明LinkedHashMap的排序方式为访问排序(插入排序是被插入的数据会存储在尾部),那么每次被通过get()访问的数据都会插入到头部 Bitmap的加载与缓存 CSDN Bitmap的加载和缓存 简书

如果系统想找一个ldpi(低密度)的资源,但找不到。那么系统会等比缩小hdpi的资源,为什么不寻找mdpi呢?因为系统对于hdpi更容易缩放,它的系数为0.5,相比mdpi的0.75来说。0.5的的性价比更高(方便计算,正确率也高一点) Android drawable的自动缩放 Android 适配(一) 单位介绍 Android 适配(drawable文件夹)图片适配(二) Android加载drawable中图片后自动缩放的原理

就目前来讲,最佳放置图片资源的文件夹就是drawable-xxhdpi Android drawable微技巧,你所不知道的drawable的那些细节 guolin

质量压缩是保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目的。 图片的长,宽,像素都不会改变,那么bitmap所占内存大小是不会变的。 质量压缩对png格式的图片没有作用, 因为png格式的图片是 无损压缩的。 质量压缩是耗时的操作,故一般不能放在主线程中进行操作,可能会导致ANR。 android中Bitmap缩放、压缩、占用内存计算等 Android Bitmap加载内存占用彻底分析

质量压缩一般可用于上传大图前的处理,这样就可以节省一定的流量,毕竟现在的手机拍照都能达到3M左右了,尺寸压缩一般可用于生成缩略图。 Android 图片质量压缩与尺寸压缩的区别

Android开发——常见的内存泄漏以及解决方案(一) Android开发——常见的内存泄漏以及解决方案(二)

所谓density,是指屏幕上的像素密度,以160dip为标准密度。举例来说,当我们在布局文件layout.xml中放置一个View

该View在密度为160dip的屏幕上显示的长度为100px(像素)长,而在320dip的屏幕上它的长度将为200px。 Android:不同drawable文件夹的区别

更新于2020年2月29日 14:56

屏幕密度 首先,我们来介绍下两个名词:density和densityDpi,它们的含义分别如下:

density:可以理解为相对屏幕密度,我们知道,1个DIP在160dpi的屏幕上大约为1像素大小。我们以160dpi为基准线,density的值即为相对于160dpi屏幕的相对屏幕密度。比如,160dpi屏幕的density值为1, 即1dip = 1px 。320dpi屏幕的density值为2,即1dip = 2px

densityDpi:可以理解为绝对屏幕密度,也就是实际的屏幕密度值(dots per inch),比如160dpi屏幕的densityDpi值就是160 Android开发之高效加载Bitmap

如何做到与密度无关:   如果屏幕密度为160,这时dp和sp和px是一样的。1dp=1sp=1px,但如果使用px作单位,如果屏幕大小不变(假设还是3.2寸),而屏 幕密度变成了320。那么原来TextView的宽度设成160px,在密度为320的3.2 寸屏幕里看要比在密度为160的3.2寸屏幕上看短了一半。但如果设置成160dp或160sp的话。系统会自动将width属性值设置成320px的。 也就是160 * 320 / 160。其中320 / 160可称为密度比例因子。也就是说,如果使用dp和sp,系统会根据屏幕密度的变化自动进行转换。官方文档总结的计算公式为:pixels = dps * (density /160). Android上常见度量单位【xdpi、hdpi、mdpi、ldpi】解读 Android - bitmap简单总结

DPI DPI的全称是 Dots Per Inch,Inch是一个物理单位(无论在任何设备上,其大小都是固定的),所以DPI就指在一个Inch的物理长度内有多少个Dot,160DPI的屏幕就表示一个Inch包含160个Dot,320DPI的屏幕表示一个Inch有320个Dot,所以说Dot的大小是不固定的。 Android设备用DPI来表示屏幕密度(Density),屏幕密度大就表示一个Inch包含的Dot比较多。那PPI是什么呢 Android设备中,DPI 等价于 PPI 就可以了 通常我们说一个设备是多少寸时,指的是屏幕对角线(Diagonal)是多少inch,所以用对角线的像素值(px)除以对角线长度(inch),就可以计算出PPI。 在这里插入图片描述 PPI越大,图片越清晰。 DP 既然有那么多不同分辨率、不同大小的屏幕,使用PX必然会导致适配困难,为了进一步简化适配工作,Android为我们提供了一个虚拟的像素单位 - DP 或者 DIP (Density-Independent pixel),当然也可以理解为 Device-Independent Pixel。为什么说是虚拟呢,因为它的大小不是一个物理(Phisical)值,而是由操作系统根据屏幕大小和密度动态渲染出来的。

PX跟DP之间的换算关系很简单

px = dp * (dpi / 160)

SP SP 全称是 Scale-independent Pixels,用于字体大小,其概念与DP是一致的,也是为了保持设备无关。因为Android用户可以根据喜好来调整字体大小,所以要使用sp来表示字体大小。 、 详解Android开发中常用的 DPI / DP / SP Android中的单位(dp、sp、dpi)

1: 跟文件存储格式无关 2:使用inSampleSize 大图–>小图 3:通过矩阵变换来放大图片 小图–>大图 4:使用RGB_565来加载不透明的图片 5:使用.9的图片做背景 6:优先使用VectorDrawable 7:尽量使用代码实现动画而不是序列帧 Android 面试题 如何计算一张图片在内存中大小 drawable和mipmap目录 为了加快渲染速度和减少图像锯齿,贴图被处理成由一系列被预先计算和优化过的图片组成的文件,这样的贴图被称为 MIP map或者 mipmap。 Android中 mipmap与drawable区别 Bitmap 的四种压缩方式详解 详解Bitmap尺寸压缩与质量压缩 关于Android中图片大小、内存占用与drawable文件夹关系的研究与分析 一张图片资源要占用多大内存xhdpi xxhdpi 2.5:drawable、mipmap区别: 1>:图片放在drawable-xhdpi、mipmap-xhdpi,占用内存一样; 2>:把图片放在mipmap提高图片渲染速度、提高图片质量、减少CPU压力,其他没什么区别。 适配手机屏幕时,可以只切一套图 Android - 图片占用内存大小及加载解析 一定要避免使用大图片,可以使用 .9图片,因为 .9图片本身比较小, 能在自定义View中的onDraw()方法中绘制的,就尽量绘制,不要直接加载大图片; Android - 一张图片到底占用多少内存 Android-屏幕适配全攻略(绝对详细)(一) Android设备系统及屏幕分辨率统计信息汇总(截至2018年7月) Android屏幕适配全攻略(最权威的官方适配指导) Android屏幕适配-应用篇 今日头条优化版 Android 屏幕适配全攻略 Android-屏幕适配全攻略 今日头条屏幕适配方案终极版 AndroidAutoSize https://material.io/resources/devices/

public class Dp2Px { public static int dp2px(Context context, int dp) { return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5); } public static int px2dp(Context context, int px) { return (int) (px / context.getResources().getDisplayMetrics().density + 0.5); } }

+0.5是为了 浮点转int时,四舍五入,保留精度 Android屏幕适配和方案【整理】 Android中的单位转换一篇就够了 更新于2020年3月2日 早晨



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3